package cgl.narada.test;

import java.util.Vector;

import cgl.narada.protocol.Connection;
import cgl.narada.protocol.Destinations;
import cgl.narada.protocol.Gateway;
import cgl.narada.protocol.GatewayInfo;
import cgl.narada.protocol.NodeAddress;
import cgl.narada.protocol.ProtocolIDFactory;
import cgl.narada.util.ByteUtilities;

public class RouteCompute {

   /**
    * @param args
    */
   public static void main(String[] args) {
      
      RouteCompute routeCompute = new RouteCompute();
      // TODO Auto-generated method stub
      /** Testing the connectivity graph */
      int[] a6 = {1}; 
      int[] a4 = {2}; 
      int[] a5 = {3}; 
      int[] d2 = {2};
      
      NodeAddress na6, na5, na4, nd2;
      
      na6 = new NodeAddress(a6);
      na5 = new NodeAddress(a5);
      na4 = new NodeAddress(a4);
      nd2 = new NodeAddress(d2);
      
      int[] addressOfNode = {1,1,1,1};
      NodeAddress thisNodeAddress = new NodeAddress(addressOfNode);
      ProtocolIDFactory idFactory = 
        new ProtocolIDFactory(thisNodeAddress, (short) 0);

      Connection c64 = new Connection(thisNodeAddress, na6, 0, 
                na4, 0, 0, idFactory.getProtocolID());
      Connection c65 = new Connection(thisNodeAddress, na6, 0, 
                na5, 0, 0, idFactory.getProtocolID());
      
      Connection c54 = new Connection(thisNodeAddress, na5, 0, 
            na4, 0, 0, idFactory.getProtocolID());
      
      Connection ca6d2 = new Connection(thisNodeAddress, na5, 0, 
            nd2, 3, 3, idFactory.getProtocolID());
      
      GatewayInfo gatewayInfo = new GatewayInfo(3);
      gatewayInfo.setNodeAddress(thisNodeAddress);
      gatewayInfo.setVertexNode(new Gateway(na6, (short)0, true));
      

      gatewayInfo.addConnection(c64);
      gatewayInfo.addConnection(c65);
      gatewayInfo.addConnection(c54);
      gatewayInfo.addConnection(ca6d2);
      
      
      
      /** Testing the hops that exist from the hosting server node*/
      Gateway[] hops = gatewayInfo.getHops();
      for (int i=0; i< hops.length; i++) {
        System.out.println("Gateway hop ->" + hops[i]);
      }
      
      byte[] routingInfo = new byte[16];
      routingInfo[15] = (byte)4;
      gatewayInfo.getListOfValidHopsToRoute(routingInfo);


      Gateway _gatewayToReach = new Gateway(na5, (short)0, false);
      Gateway hop = gatewayInfo.getBestHopToReachNode(_gatewayToReach);
      
      System.out.println("Best hop to reach " + _gatewayToReach + " is " + hop);
      
      /** Enumerating the routing cache contents */
     gatewayInfo.enumerateBestHopsToTake();
     
     System.out.println("\n\n\n\n");
     
     Destinations fastDestinations= gatewayInfo.getFastestDestinationsFromHop(hop);
     
     System.out.println("Fastest destinations from hop [" + hop + "]\n" +
           fastDestinations + "\n\n");
     
     System.out.println("Destinations reachable from hop [" + hop + "]\n" +
           gatewayInfo.getDestinationsReachedFromHop(hop));
  
     Gateway[] hopsToTake = gatewayInfo.getHops();
     
     
     
     
     
     
     int systemLevel = 3;
     Destinations toReach = new Destinations(systemLevel);
     toReach.setDestinationsAtLevel(0, 4);
     toReach.setDestinationsAtLevel(1, 8);
     toReach.setDestinationsAtLevel(2, 16);
     toReach.setDestinationsAtLevel(3, 32);
     Destinations traversedSoFar = new Destinations(systemLevel);
     System.out.println("To Reach " + toReach);
     
     //routeCompute.checkIfDestinationsCanBeFastTracked(toReach, fastDestinations);

     
     for (int i=systemLevel; i>= 0; i--) {
        int destination = toReach.getDestinationsAtLevel(i);
        String destVal = ByteUtilities.printInt(destination);
        //System.out.println("Destination at level (" + i + ") = " + destVal);
     }
     
     
     Gateway[] _hops = gatewayInfo.hopsToReachDestination(toReach, traversedSoFar);
     
     if (_hops != null) {
        
        System.out.println("hops.length = " + _hops.length + " To Reach " + toReach +
              "Traversed So Far = " + traversedSoFar);
        for (int i=0; i < _hops.length; i++) {
           System.out.println("Hops to take = " + _hops[i]);
        }
     }
     
     
     
     
     for (int i=0; i<=systemLevel; i++) {
        Vector levelGateways = gatewayInfo.listTheGatewaysWithinUnitAtLevel(i);
        //System.out.println("\tGateways at level (" + i + ") = " + levelGateways);
     }
     
     for (int j=0; j<= systemLevel; j++) {
        int level = j;
        Destinations toPropagateTo = new Destinations(systemLevel);
        for (int i=0; i<= level; i++ ) {
           toPropagateTo.setDestinationsAtLevel(i, toReach.getDestinationsAtLevel(i));
        }
        System.out.println("Level (" + level + ") profile update to be sent to \n" +
              toPropagateTo);
     }
   }
   
   public boolean 
   checkIfDestinationsCanBeFastTracked(Destinations destinationsToReach,
                                       Destinations fastTracked) {
      int systemLevel = 3;
      int[] reachDests = destinationsToReach.getDestinationsInInts();
      int[] fastTrack = fastTracked.getDestinationsInInts();
      
      boolean canReach = false;
      for (int i=0; i<= systemLevel; i++) {
         int andValue = reachDests[i] & fastTrack[i];
         if ( andValue != 0 ) {
            canReach = true;
            /** Some destinations can be fast tracked */
            int cannotReach = reachDests[i] & ByteUtilities.performNotOperation(fastTrack[i]);
            System.out.println("Can Reach =>" + ByteUtilities.printInt(andValue));
            System.out.println("Cannot Reach=>" + ByteUtilities.printInt(cannotReach));
         }
            
      }
      return canReach;
   }

}
